package org.study.id.other.util;

import lombok.extern.slf4j.Slf4j;

/**
 * 分布式ID生成算法：基于SnowFlake
 * 参考 https://segmentfault.com/a/1190000011282426
 *
 * @author doveylovey
 * @version v1.0.0
 * @email 1135782208@qq.com
 * @date 2023年04月22日
 */
@Slf4j
public class SnowflakeIdWorker {
    /**
     * 起始的时间戳
     */
    private long twepoch = 1288834974657L;
    /**
     * 每一部分占用的位数：序列号占用12位、机器标识占用5位、数据中心占用5位
     */
    private long sequenceBits = 12L;
    private long workerIdBits = 5L;
    private long datacenterIdBits = 5L;

    /**
     * 每一部分的最大值
     */
    private long sequenceMask = -1L ^ (-1L << sequenceBits);
    private long maxWorkerId = -1L ^ (-1L << workerIdBits);
    private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);

    /**
     * 每一部分向左的位移
     */
    private long workerIdShift = sequenceBits;
    private long datacenterIdShift = sequenceBits + workerIdBits;
    private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;

    /**
     * 上一次时间戳
     */
    private long lastTimestamp = -1L;
    /**
     * 机器标识
     */
    private long workerId;
    /**
     * 数据中心
     */
    private long datacenterId;
    /**
     * 序列号
     */
    private long sequence;

    public static SnowflakeIdWorker of(long workerId, long datacenterId, long sequence) {
        return new SnowflakeIdWorker(workerId, datacenterId, sequence);
    }

    private SnowflakeIdWorker(long workerId, long datacenterId, long sequence) {
        // 校验workerId
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
        }
        // 校验datacenterId
        if (datacenterId > maxDatacenterId || datacenterId < 0) {
            throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
        }
        log.info("雪花算法初始参数：workerId={}，datacenterId={}，sequence={}", workerId, datacenterId, sequence);
        this.workerId = workerId;
        this.datacenterId = datacenterId;
        this.sequence = sequence;
    }

    private long tilNextMillis(long lastTimestamp) {
        long timestamp = this.timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = this.timeGen();
        }
        return timestamp;
    }

    private long timeGen() {
        return System.currentTimeMillis();
    }

    public synchronized long nextId() {
        long timestamp = this.timeGen();
        if (timestamp < lastTimestamp) {
            log.error("clock is moving backwards. Rejecting requests until " + lastTimestamp);
            throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
        }
        if (lastTimestamp == timestamp) {
            // 相同毫秒内，序列号自增
            sequence = (sequence + 1) & sequenceMask;
            // 同一毫秒的序列数已经达到最大
            if (sequence == 0) {
                timestamp = this.tilNextMillis(lastTimestamp);
            }
        } else {
            // 不同毫秒内，序列号置为0
            sequence = 0;
        }
        lastTimestamp = timestamp;
        // 时间戳部分、数据中心部分、机器标识部分、序列号部分
        return ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift) | (workerId << workerIdShift) | sequence;
    }

    /*public static void main(String[] args) {
        long count = 10000000L;
        Set<Long> result = Sets.newHashSet();
        SnowFlakeIdWorker idWorker = SnowFlakeIdWorker.of(1, 1, 1);
        long start = System.currentTimeMillis();
        for (long i = 0; i < count; i++) {
            long id = idWorker.nextId();
            result.add(id);
        }
        System.out.println("计划生成ID数量：" + count + "，实际生成ID数量：" + result.size() + "，耗时：" + (System.currentTimeMillis() - start));
    }*/
}
